import time
from fastapi import Request, Response
from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware


class AuditorMiddleware(BaseHTTPMiddleware):
    """请求审计记录"""
    def __init__(self, app, auditor, ignore_method=('GET', 'HEAD')):
        self.auditor = auditor
        self.ignore_method = ignore_method
        super().__init__(app)

    @staticmethod
    def http_auditor(func, request, status_code, cost):
        if not (route := request.scope.get('route')):
            return
        record = dict(
                type='http',
                module=route.openapi_extra.get('module', ''),
                sub_module=route.openapi_extra.get('sub_module', ''),
                summary=route.summary,
                func=route.name,
                method=request.method.lower(),

                ip=(request.headers.get('X-Real-IP') or request.headers.get('X-Forwarded-For') or request.client.host),
                name=request.state.user.get('name', '') if getattr(request.state, 'user', None) else ''
            )

        detail = getattr(request.state, 'detail', '')  # 异常原因
        func(**record, status_code=status_code, cost=cost, detail=detail)

    async def dispatch(self, request: Request, call_next):
        start = time.time()
        try:
            response: Response = await call_next(request)
        except Exception as e:
            response = JSONResponse(status_code=500, content={'code': -1, 'message': '系统异常请联系管理员'})
        finally:
            cost = int((time.time() - start) * 1000)  # ms
            if response.status_code in range(200, 400):
                if request.method in self.ignore_method:
                    return response

                self.http_auditor(getattr(self.auditor, 'info'), request, status_code=response.status_code, cost=cost)
                return response
            self.http_auditor(getattr(self.auditor, 'error'), request, status_code=response.status_code, cost=cost)
            return response


async def validation_exception_handler(request, exc, *args, **kwargs):
    """参数校验异常"""
    request.state.detail = '请求参数异常'
    return JSONResponse(status_code=422, content={'code': -1, 'message': '请检查参数'})


async def http_exception_handler(request, exc, *args, **kwargs):
    """http异常记录与捕获"""
    request.state.detail = exc.detail
    return JSONResponse(status_code=exc.status_code, content={'code': -1, 'message': exc.detail})
